package com.example.pivotgauss;

import java.io.InputStream;
import java.text.DecimalFormat;
import java.util.Random;

import org.xml.sax.ext.LexicalHandler;

import android.R.anim;
import android.app.Activity;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
import android.os.Message;
import android.support.v4.widget.SlidingPaneLayout;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.ScrollView;
import android.widget.TextView;

import com.jjoe64.graphview.GraphView;
import com.jjoe64.graphview.GraphView.LegendAlign;
import com.jjoe64.graphview.GraphViewDataInterface;
import com.jjoe64.graphview.GraphViewSeries;
import com.jjoe64.graphview.GraphViewSeries.GraphViewSeriesStyle;
import com.jjoe64.graphview.LineGraphView;
import com.jjoe64.graphview.ValueDependentColor;

import fr.vod.gauss.File.File;
import fr.vod.gauss.File.FileImpl;
import fr.vod.gauss.encoder.Encoder;
import fr.vod.gauss.encoder.EncoderImp;
import fr.vod.gauss.matrice.Matrice;
import fr.vod.gauss.matrice.MatriceImpl;
import fr.vod.gauss.matrice.PivotDeGauss;
import fr.vod.gauss.matrice.PivotDeGaussImpl;
import fr.vod.gauss.net.Format;
import fr.vod.gauss.net.FormatImpl;
import fr.vod.gauss.operations.CorpsDeGalloisOperation;

public class MainActivity extends Activity implements OnClickListener {
	
	// Elements fixes
	private Button generateButton;
	private Button reinitializeButton;
	private EditText eStep;
	private EditText eMax;
	
	// Elements de la premiere vue
	private EditText eBlock;
	private EditText eInitPacket;
	private EditText eMaxPacket;

	// Elements de la seconde vue
	private EditText ePacket;
	private EditText eInitBlock;
	private EditText eMaxBlock;

	/* Elements du graphique */
	private GraphViewSeries dataSeries1;
	private GraphViewSeries dataSeries2;
	private GraphView graphView1;
	private GraphView graphView2;
	private GraphViewData[] graphDatas1;
	private GraphViewData[] graphDatas2;

	/* Elements de l'UI */
	private LinearLayout layout1;
	private LinearLayout layout2;
	private LinearLayout progressLayout;
	private SlidingPaneLayout pane;
	private ProgressBar mProgress;
	private TextView mProgressText;
	private TextView mInfoProgressText;
	private ScrollView scrollView;
	
	/* Calcul du temps pour générer un graph */
	private long beginGeneration;
	private long endGeneration;
	
	private Random random;

	// Gère les communications avec le thread de téléchargement
	final private Handler mHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			super.handleMessage(msg);
			Bundle bundle = msg.getData();
			boolean firstGraph = bundle.getBoolean("firstGraph");
			boolean displayGraph = bundle.getBoolean("shouldDisplay");

			if (firstGraph) {
				if (!displayGraph) {
					Result r = (Result) msg.obj;

					graphDatas1[r.id] = new GraphViewData(r.packetSize,
							r.packetOK);
					int progress = (int) ((double) ((double) r.id / (double) graphDatas1.length) * 100);
					mProgress.setProgress(progress);
					mProgressText.setText(progress + " %");
					appendToProgress("Adding data for packet size = " + r.packetSize + " (" + new DecimalFormat("##.##")
					.format(r.duree)+" s)");
				} else {
					mProgress.setProgress(100);
					mProgressText.setText(100 + " %");
					
					// On genere une couleur aleatoire
					Paint p = new Paint();
					p.setARGB(255, random.nextInt(255), random.nextInt(255), random.nextInt(255));
					
					// On creer la serie et on l'ajoute au graph
					dataSeries1 = new GraphViewSeries("Block size " + eBlock.getText().toString(), new GraphViewSeriesStyle(p.getColor(), 5), graphDatas1);
					graphView1.addSeries(dataSeries1);
					
					endGeneration = System.nanoTime();
					appendToProgress("\nData generation ended !");
					appendToProgress("Time consumed : " + new DecimalFormat("##.##").format((endGeneration - beginGeneration) * Math.pow(10, -9)) + " seconds.");
					appendToProgress("\n");
				}
			} else {
				if (!displayGraph) {
					Result r = (Result) msg.obj;
					graphDatas2[r.id] = new GraphViewData(r.blockSize,
							r.duree);
					int progress = (int) ((double) ((double) r.id / (double) graphDatas2.length) * 100);
					mProgress.setProgress(progress);
					mProgressText.setText(progress + " %");
					appendToProgress("Adding data for block size = " + r.blockSize + " (" + new DecimalFormat("##.##")
					.format(r.duree)+" s)");
				} else {
					mProgress.setProgress(100);
					mProgressText.setText(100 + " %");
					// On genere une couleur aleatoire
					Paint p = new Paint();
					p.setARGB(255, random.nextInt(255), random.nextInt(255), random.nextInt(255));
					
					// On creer la serie et on l'ajoute au graph
					dataSeries2 = new GraphViewSeries("Packet size " + ePacket.getText().toString(), new GraphViewSeriesStyle(p.getColor(), 5), graphDatas2);
					
					graphView2.addSeries(dataSeries2);
					
					endGeneration = System.nanoTime();
					appendToProgress("\nData generation ended !");
					appendToProgress("Time consumed : " + new DecimalFormat("##.##").format((endGeneration - beginGeneration) * Math.pow(10, -9)) + " seconds.");
					appendToProgress("\n");
				}
			}

		}
	};
	
	protected void appendToProgress (String text) {
		mInfoProgressText.append("\n" + text);
		scrollView.fullScroll(View.FOCUS_DOWN);
	}
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		Log.i("OS MOdEl", "Build = " + android.os.Build.MODEL);
		if (android.os.Build.MODEL.contains("Nexus")) {
			Log.i("BLU", "ON INSTANCIE LE NEXUS LAYOUT !!!");
			setContentView(R.layout.activity_main_nexus);
		}
		else
			setContentView(R.layout.activity_main);

		layout1 = (LinearLayout) findViewById(R.id.graphicLayout1);
		layout2 = (LinearLayout) findViewById(R.id.graphicLayout2);
		layout2.setVisibility(View.INVISIBLE);

		initializeLeftPanel();
		initializeGraphs();
		initializeSlidingPanel();
		progressLayout.setVisibility(View.INVISIBLE);
		
		random = new Random();
	}

	private void initializeSlidingPanel() {
		pane = (SlidingPaneLayout) findViewById(R.id.slidingPane1);
		pane.setPanelSlideListener(new PaneListener());
		pane.openPane();
	}

	private void initializeLeftPanel() {
		eBlock = (EditText) findViewById(R.id.blockSize);
		eInitPacket = (EditText) findViewById(R.id.initPacketSize);
		eMaxPacket = (EditText) findViewById(R.id.maxPacketSize);
		
		ePacket = (EditText) findViewById(R.id.packetSize);
		eInitBlock = (EditText) findViewById(R.id.initBlockSize);
		eMaxBlock = (EditText) findViewById(R.id.maxBlockSize);
		
		eStep = (EditText) findViewById(R.id.step);
		eMax = (EditText) findViewById(R.id.max);
		generateButton = (Button) findViewById(R.id.generateButton);
		generateButton.setOnClickListener(this);
		
		scrollView = (ScrollView) findViewById(R.id.scrollViewInfo);
		mInfoProgressText = (TextView) findViewById(R.id.infoTextView);
		mProgress = (ProgressBar) findViewById(R.id.progressBar);
		mProgress.setProgress(0);
		mProgress.setProgressDrawable(getResources().getDrawable(R.drawable.my_progress_bar));
		mProgressText = (TextView) findViewById(R.id.progressText);
		
		progressLayout = (LinearLayout) findViewById(R.id.progressLayout);
		reinitializeButton = (Button) findViewById(R.id.reinitButton);
		reinitializeButton.setOnClickListener(this);
	}

	private void initializeGraphs() {
		graphView1 = new LineGraphView(this, "Success rate (%) depending on selection");
		//graphView1.addSeries(dataSeries); // data

		graphView2 = new LineGraphView(this, "Build time (ms) for 1 byte depending on block size");
		//graphView2.addSeries(dataSeries); // data

		graphView1.setScalable(true);

		layout1.addView(graphView1);
		layout2.addView(graphView2);
		
		//	((LineGraphView) graphView1).setDrawBackground(true);
		graphView1.setManualYAxisBounds(100, 0);
		//graphView2.setManualYAxisBounds(20, 0);

		setGraphsStyles();
	}

	private void setGraphsStyles() {
		// set styles
		graphView1.getGraphViewStyle().setGridColor(Color.BLACK);
		graphView1.getGraphViewStyle().setHorizontalLabelsColor(Color.BLACK);
		graphView1.getGraphViewStyle().setVerticalLabelsColor(Color.BLACK);
		graphView1.getGraphViewStyle().setNumVerticalLabels(5);
		
		graphView1.setShowLegend(true);
		graphView1.setLegendAlign(LegendAlign.MIDDLE);
		graphView1.setLegendWidth(250);

		// set styles
		graphView2.getGraphViewStyle().setGridColor(Color.BLACK);
		graphView2.getGraphViewStyle().setHorizontalLabelsColor(Color.BLACK);
		graphView2.getGraphViewStyle().setVerticalLabelsColor(Color.BLACK);
		
		graphView2.setShowLegend(true);
		graphView2.setLegendAlign(LegendAlign.MIDDLE);
		graphView2.setLegendWidth(250);
	}

	@Override
	public void onClick(View v) {
		mProgress.setProgress(0);
		mProgressText.setText("");
		
		switch (v.getId()) {
		case R.id.generateButton:
			if (!progressLayout.isShown())
				progressLayout.setVisibility(View.VISIBLE);
			
			appendToProgress("\n*** Loading New Sample ***");
			
			beginGeneration = System.nanoTime();
			if (pane.isOpen()) {
				// Pour le graphe de % succes / selection
				startSuccessThread();
			} else {
				// Pour le temps de d�codage d'un paquet / block size 
				startTimeThread();
			}
			break;
		case R.id.reinitButton:
			graphView1.removeAllSeries();
			graphView2.removeAllSeries();
			mInfoProgressText.setText("");
			progressLayout.setVisibility(View.INVISIBLE);
			break;
		default:
			break;
		}
		
	}

	private void startTimeThread() {
		int serieSize = (Integer.valueOf(eMaxBlock.getText().toString()) - Integer.valueOf(eInitBlock.getText().toString())) / Integer.valueOf(eStep.getText().toString()); // (max - init) / step
		graphDatas2 = new GraphViewData[serieSize+1];
		//graphView2.removeAllSeries();
		
		new Thread(new Runnable() {

			@Override
			public void run() {
				int BLOCK = Integer.valueOf(eInitBlock.getText().toString());
				int BLOCK_MAX = Integer.valueOf(eMaxBlock.getText().toString());
				int PACKET = Integer.valueOf(ePacket.getText().toString());
				int STEP = Integer.valueOf(eStep.getText().toString());
				int MAX = Integer.valueOf(eMax.getText().toString());
				int stat_packet[] = new int[MAX];
				long stat_duree[] = new long[MAX];
				int cptId = 0;
				
				Encoder en = new EncoderImp(new CorpsDeGalloisOperation());
				InputStream fis;

				do {
					try {
						fis = getResources().openRawResource(R.raw.dragonball);

						File pi = new FileImpl(fis, en, BLOCK, PACKET);
						Format f = new FormatImpl();
						Matrice m;
						PivotDeGauss pdg = new PivotDeGaussImpl(
								new CorpsDeGalloisOperation());
						boolean resolu = false;

						pi.readNextBlock();

						int cpt_packet = 0;
						long debut, fin = 0;
						int cpt_packet_ok = 0;
						// Boucle de test
						for (int k = 0; k < MAX; k++) {
							cpt_packet = BLOCK;
							m = new MatriceImpl();
							for (int i = 0; i < cpt_packet; i++) {
								m.addEquation(f.format(pi.create(), BLOCK,
										PACKET));
							}
							debut = System.nanoTime();
							resolu = pdg.resoudre(m);

							cpt_packet_ok += resolu == true ? 1 : 0;

							while (!resolu && m.getLength() < BLOCK) {
								m.addEquation(f.format(pi.create(), BLOCK,
										PACKET));
								resolu = pdg.resoudre(m);
								cpt_packet++;

							}
							
							fin = System.nanoTime();
							stat_duree[k] = fin - debut; // Temps pour un bloc
							stat_packet[k] = cpt_packet;
							
						}

						int sum_packet = 0;
						long sum_duree = 0;
						// Boucle récupérant les données
						
						for (int i = 0; i < MAX; i++) {
							sum_packet += stat_packet[i];
							sum_duree += stat_duree[i]; // moyenne de la durée pour chaque bloc
						}
						
						int avg_packet = (int) Math.ceil((double) sum_packet/ MAX);
						sum_duree = (long) (sum_duree * Math.pow(10, -6));
						double avg_durre = (double) (sum_duree)/(sum_packet);
						
						
						Message msg = mHandler.obtainMessage(0, new Result(cptId,
								avg_packet, avg_durre,
								PACKET, ((double)cpt_packet_ok/MAX)*100, BLOCK));
						Bundle bundle = msg.getData();
						bundle.putBoolean("firstGraph", false);
						bundle.putBoolean("shouldDisplay", false);
						mHandler.sendMessage(msg);
					} catch (Exception e) {
						e.printStackTrace();
					}
				BLOCK += STEP;
				cptId += 1;
				} while (BLOCK <= BLOCK_MAX);
				// Pour finir, on demande d'afficher le graph généré
				Message msg = mHandler.obtainMessage(0, null);
				Bundle bundle = msg.getData();
				bundle.putBoolean("firstGraph", false);
				bundle.putBoolean("shouldDisplay", true);
				mHandler.sendMessage(msg);
			}
		}).start();
		
	}

	private void startSuccessThread() {
		int serieSize = (Integer.valueOf(eMaxPacket.getText().toString()) - Integer.valueOf(eInitPacket.getText().toString())) / Integer.valueOf(eStep.getText().toString()); // (max - init) / step
		graphDatas1 = new GraphViewData[serieSize+1];
		//graphView1.removeAllSeries();
		
		new Thread(new Runnable() {

			@Override
			public void run() {
				int BLOCK = Integer.valueOf(eBlock.getText().toString());
				int PACKET = Integer.valueOf(eInitPacket.getText().toString());
				int PACKET_MAX = Integer.valueOf(eMaxPacket.getText()
						.toString());
				int STEP = Integer.valueOf(eStep.getText().toString());
				int MAX = Integer.valueOf(eMax.getText().toString());
				int stat_packet[] = new int[MAX];
				long stat_duree[] = new long[MAX];
				int cptId = 0;
				
				Encoder en = new EncoderImp(new CorpsDeGalloisOperation());
				InputStream fis;

				do {
					try {
						fis = getResources().openRawResource(R.raw.dragonball);

						File pi = new FileImpl(fis, en, BLOCK, PACKET);
						Format f = new FormatImpl();
						Matrice m;
						PivotDeGauss pdg = new PivotDeGaussImpl(
								new CorpsDeGalloisOperation());
						boolean resolu = false;

						pi.readNextBlock();

						int cpt_packet = 0;
						long debut, fin;
						int cpt_packet_ok = 0;
						// Boucle de test
						for (int k = 0; k < MAX; k++) {
							cpt_packet = BLOCK;
							m = new MatriceImpl();
							for (int i = 0; i < cpt_packet; i++) {
								m.addEquation(f.format(pi.create(), BLOCK,
										PACKET));
							}
							debut = System.nanoTime();
							resolu = pdg.resoudre(m);

							cpt_packet_ok += resolu == true ? 1 : 0;

							while (!resolu && m.getLength() < BLOCK) {
								m.addEquation(f.format(pi.create(), BLOCK,
										PACKET));
								resolu = pdg.resoudre(m);
								cpt_packet++;
							}
							fin = System.nanoTime();
							stat_duree[k] = fin - debut;
							stat_packet[k] = cpt_packet;
						}

						int sum_packet = 0;
						long sum_duree = 0;
						// Boucle récupérant les données
						for (int i = 0; i < MAX; i++) {
							sum_packet += stat_packet[i];
							sum_duree += stat_duree[i];
						}

						int avg_packet = (int) Math.ceil((double) sum_packet
								/ MAX);
						int avg_durre = (int) Math.ceil((double) sum_duree
								/ MAX);
						
						Message msg = mHandler.obtainMessage(0, new Result(cptId,
								avg_packet, avg_durre * Math.pow(10, -9),
								PACKET, ((double)cpt_packet_ok/MAX)*100, BLOCK));
						Bundle bundle = msg.getData();
						bundle.putBoolean("firstGraph", true);
						bundle.putBoolean("shouldDisplay", false);
						mHandler.sendMessage(msg);
					} catch (Exception e) {
						e.printStackTrace();
					}
				PACKET += STEP;
				cptId += 1;
				} while (PACKET <= PACKET_MAX);
				// Pour finir, on demande d'afficher le graph généré
				Message msg = mHandler.obtainMessage(0, null);
				Bundle bundle = msg.getData();
				bundle.putBoolean("firstGraph", true);
				bundle.putBoolean("shouldDisplay", true);
				mHandler.sendMessage(msg);
			}
		}).start();
		
	}

	/**
	 * Classes internes
	 */
	private class Result {
		public double duree;
		public long requis;
		public long packetSize;
		public double packetOK;
		public int id;
		private int blockSize;

		public Result(int id, long requis, double duree, long packetSize, double packetOk, int blockSize) {
			this.id = id;
			this.duree = duree;
			this.requis = requis;
			this.packetSize = packetSize;
			this.packetOK = packetOk;
			this.blockSize = blockSize;
		}
	}

	public class GraphViewData implements GraphViewDataInterface {
		private double x, y;

		public GraphViewData(double packetSize, double packetOK) {
			this.x = packetSize;
			this.y = packetOK;
		}

		@Override
		public double getX() {
			return this.x;
		}

		@Override
		public double getY() {
			return this.y;
		}
	}

	private class PaneListener implements SlidingPaneLayout.PanelSlideListener {

		private boolean hasMoved = false;

		@Override
		public void onPanelClosed(View view) {
			// On instancie les layout a modifier
			LinearLayout layout1 = (LinearLayout) findViewById(R.id.graphicLayout1);
			LinearLayout layout2 = (LinearLayout) findViewById(R.id.graphicLayout2);
			
			LinearLayout paramLayout1 = (LinearLayout) findViewById(R.id.paramLayout1);
			LinearLayout paramLayout2 = (LinearLayout) findViewById(R.id.paramLayout2);
			
			EditText eStep = (EditText) findViewById(R.id.step);
			EditText eMax = (EditText) findViewById(R.id.max);

			if (hasMoved) {
				layout1.setVisibility(View.INVISIBLE);
				layout1.setFocusable(false);
				layout1.setClickable(false);
				
				paramLayout1.setVisibility(View.INVISIBLE);
				paramLayout1.setFocusable(false);
				paramLayout1.setClickable(false);

				layout2.setVisibility(View.VISIBLE);
				paramLayout2.setVisibility(View.VISIBLE);
				paramLayout2.setFocusable(true);
				paramLayout2.setClickable(true);
				
				eStep.invalidate();
				eMax.invalidate();

				hasMoved=false;
			}

		}

		@Override
		public void onPanelOpened(View view) {
			// On instancie les layout a modifier
			LinearLayout graphicLayout1 = (LinearLayout) findViewById(R.id.graphicLayout1);
			LinearLayout graphicLayout2 = (LinearLayout) findViewById(R.id.graphicLayout2);
			
			LinearLayout paramLayout1 = (LinearLayout) findViewById(R.id.paramLayout1);
			LinearLayout paramLayout2 = (LinearLayout) findViewById(R.id.paramLayout2);
			
			EditText eStep = (EditText) findViewById(R.id.step);
			EditText eMax = (EditText) findViewById(R.id.max);
			
			if (hasMoved) {
				graphicLayout2.setVisibility(View.INVISIBLE);
				graphicLayout2.setFocusable(false);
				graphicLayout2.setClickable(false);
				
				paramLayout2.setVisibility(View.INVISIBLE);
				paramLayout2.setFocusable(false);
				paramLayout2.setClickable(false);
				
				
				graphicLayout1.setVisibility(View.VISIBLE);
				paramLayout1.setVisibility(View.VISIBLE);
				paramLayout1.setFocusable(true);
				paramLayout1.setClickable(true);

				eStep.invalidate();
				eMax.invalidate();
				hasMoved = false;
			}
		}

		@Override
		public void onPanelSlide(View view, float arg1) {
			// LogHelper.entree("PaneListener", "Panel slide");
			hasMoved = true;
		}

	}

}
